package com.eastfair.imaster.service.impl;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.eastfair.annotation.base.SubjectTypeEnum;
import com.eastfair.boot.service.SuperCacheServiceImpl;
import com.eastfair.cache.intraction.CollectionCacheKeyBuilder;
import com.eastfair.cache.redis.RedisOps;
import com.eastfair.constant.BusinessConstant;
import com.eastfair.core.base.R;
import com.eastfair.core.context.ContextUtil;
import com.eastfair.core.model.CacheKeyBuilder;
import com.eastfair.core.snowflake.DefaultUidGenerator;
import com.eastfair.enums.RedisKeyEnum;
import com.eastfair.imaster.dao.CollectionMapper;
import com.eastfair.imaster.dto.CollectionDTO;
import com.eastfair.imaster.entity.Collection;
import com.eastfair.imaster.service.CollectionService;
import com.eastfair.projectcore.api.ProjectCoreServiceFeign;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 * 业务实现类
 * 收藏表
 * </p>
 *
 * @author liuxiaobai
 * @date 2021-08-31
 */
@Slf4j
@Service
@DS("#thread.tenant")
public class CollectionServiceImpl extends SuperCacheServiceImpl<CollectionMapper, Collection> implements CollectionService {

    @Autowired
    private ProjectCoreServiceFeign projectCoreServiceFeign;
    @Autowired
    private RedisOps redisOps;

    @Override
    protected R<Boolean> handlerSave(Collection model) {
        //项目id
        model.setProjectId(ContextUtil.getProjectId());
        //雪花ID
        model.setId(DefaultUidGenerator.getUidOfProjectId(model.getProjectId()));
        return R.successDef();
    }

    @Override
    protected CacheKeyBuilder cacheKeyBuilder() {
        return new CollectionCacheKeyBuilder();
    }

    /**************sharding 分库分表操作 *****************/
    @Override
    public boolean updateByIdOfShard(Collection entity) {
        UpdateWrapper<Collection> updateWrapper = new UpdateWrapper<>();
        //可将指定字段更新为null
        updateWrapper.lambda().eq(Collection::getProjectId, ContextUtil.getProjectId()).eq(Collection::getId, entity.getId());
        entity.setProjectId(null);
        delCache(entity);
        return SqlHelper.retBool(this.getBaseMapper().update(entity, updateWrapper));
    }

    @Override
    public void saveCollection(CollectionDTO collectionDTO) {
        String cacheKey = String.format(RedisKeyEnum.COLLECTION_NUM_EXHIBITION.getKey(), ContextUtil.getExhibitionId(), collectionDTO.getSubjectType().name());
        //查询有没有收藏过
        Long id = check(collectionDTO.getSubjectType(), collectionDTO.getSubjectId());
        if (id != null) {
            //已收藏时 取消收藏
            updateByIdOfShard(Collection.builder()
                    .id(id)
                    .isDelete(BusinessConstant.DELETE_YES)
                    .build());
            //收藏数-1
            this.redisOps.zIncrBy(cacheKey, collectionDTO.getSubjectId(), -1);
            return;
        }
        Collection collection = Collection.builder()
                .projectId(ContextUtil.getProjectId())
                .exhibitionId(ContextUtil.getExhibitionId())
                .userType(ContextUtil.getSubjectType())
                .userId(ContextUtil.getUserId())
                .subjectType(collectionDTO.getSubjectType().name())
                .subjectId(collectionDTO.getSubjectId())
                .build();
        super.save(collection);
        //收藏数+1
        this.redisOps.zIncrBy(cacheKey, collectionDTO.getSubjectId(), 1);
    }

    @Override
    public Long check(SubjectTypeEnum subjectType, Long subjectId) {
        QueryWrapper<Collection> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(Collection::getProjectId, ContextUtil.getProjectId())
                .eq(Collection::getUserId, ContextUtil.getUserId())
                .eq(Collection::getSubjectType, subjectType.name())
                .eq(Collection::getSubjectId, subjectId)
                .eq(Collection::getIsDelete, BusinessConstant.DELETE_NO);
        Collection collection = super.getOne(queryWrapper, false);
        return collection == null ? null : collection.getId();
    }

    @Override
    public CollectionDTO checkWithNum(SubjectTypeEnum subjectType, Long subjectId) {
        String cacheKey = String.format(RedisKeyEnum.COLLECTION_NUM_EXHIBITION.getKey(), ContextUtil.getExhibitionId(), subjectType.name());
        Double num = this.redisOps.zScore(cacheKey, subjectId);
        return CollectionDTO.builder()
                .isCollect(check(subjectType, subjectId) != null)
                .num(num == null ? 0 : num.longValue())
                .build();
    }

    @Override
    public Set<Long> checkBatch(SubjectTypeEnum subjectType, List<Long> subjectIds) {
        QueryWrapper<Collection> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .select(Collection::getSubjectId)
                .eq(Collection::getProjectId, ContextUtil.getProjectId())
                .eq(Collection::getUserId, ContextUtil.getUserId())
                .eq(Collection::getSubjectType, subjectType.name())
                .in(Collection::getSubjectId, subjectIds)
                .eq(Collection::getIsDelete, BusinessConstant.DELETE_NO);
        List<Collection> collectionList = super.list(queryWrapper);
        return collectionList.stream().map(Collection::getSubjectId).collect(Collectors.toSet());
    }

    @Override
    public Map<Long, CollectionDTO> checkBatchWithNum(SubjectTypeEnum subjectType, List<Long> subjectIds) {
        String cacheKey = String.format(RedisKeyEnum.COLLECTION_NUM_EXHIBITION.getKey(), ContextUtil.getExhibitionId(), subjectType.name());
        Set<Long> ids = checkBatch(subjectType, subjectIds);
        Map<Long, CollectionDTO> result = new HashMap<>(subjectIds.size());
        subjectIds.forEach(id -> {
            Double num = this.redisOps.zScore(cacheKey, id);
            result.put(id, CollectionDTO.builder()
                    .isCollect(ids.contains(id))
                    .num(num == null ? 0 : num.longValue())
                    .build());
        });
        return result;
    }

}
